package one.chchy.demo.sprintsecurity.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.token.TokenEnhancer;
import org.springframework.security.oauth2.provider.token.TokenEnhancerChain;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;

//@Order(2)
@Configuration
@EnableAuthorizationServer
public class ImoocAuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {

    @Autowired
    private PasswordEncoder passwordEncoder;

    @Resource(name = "demoUserDetail")
    private UserDetailsService userDetailsService;

    @Autowired
    private AuthenticationManager authenticationManager;

    @Autowired
    private JwtAccessTokenConverter jwtAccessTokenConverter;

    @Autowired
    private TokenEnhancer jwtTokenEnhancer;

    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
        super.configure(security);

        // 经过测试，这个接口不用调用也是可以的，但是有时候如果没有配置，在获取token的时候会碰到401错误
        // 原先的理解是以为这个接口调用后，可以使用post 的形式获取token，但是经过测试后发现不是，具体原因以后在
        // 了解，参考链接：https://blog.csdn.net/qq_25497867/article/details/81201532 https://blog.csdn.net/u012040869/article/details/80140515
        security.allowFormAuthenticationForClients();
        // 默认值是denyAll,不允许访问，修改为permitAll()才能支持判断token是否有效的接口 https://github
        // .com/royclarkson/spring-rest-service-oauth/issues/28 https://www.jianshu.com/p/c1297003c903
        security.checkTokenAccess("permitAll()");
    }

    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        TokenEnhancerChain enhancerChain = new TokenEnhancerChain();
        List<TokenEnhancer> tokenEnhancers = new ArrayList();
        tokenEnhancers.add(jwtTokenEnhancer);
        tokenEnhancers.add(jwtAccessTokenConverter);
        enhancerChain.setTokenEnhancers(tokenEnhancers);

        // IllegalStateException, UserDetailsService is required
        // 授权码模式下报这个错误是因为需要在这里配置user details service类
        // 如果没有配置这个，在获取授权码，获取token，判断token是否失效上没有问题，但是在刷新token的时候会出现问题
        endpoints.userDetailsService(userDetailsService)
                // 授权码模式授权网页出现改变，不知道是不是这个配置产生的变化
                .authenticationManager(authenticationManager)
                .tokenEnhancer(enhancerChain)
                .accessTokenConverter(jwtAccessTokenConverter);
    }

    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
//        super.configure(clients);
        clients.inMemory()
                .withClient("demo")
                .secret(passwordEncoder.encode("secret"))
                .authorizedGrantTypes("authorization_code", "refresh_token", "password", "implicit")
                .scopes("all")
                .authorities("admin", "ROLE_USER")
//                这个跳转链接最后是自己实现，如果写百度的或者其他的很有可能会报uri不匹配错误
                // 在新的security接口下，这个跳转链接需要主动设置，否则将会出现 At least one redirect_uri must be registered with the client 错误
                // https://blog.csdn.net/taotaojs/article/details/85126140 而且必须与授权码模式下跳转接口一致
                .redirectUris("http://localhost:8080/hello")
                .accessTokenValiditySeconds(24 * 3600)
                .refreshTokenValiditySeconds(36 * 3600);
    }
}
